"""API Key management endpoints."""

from typing import List, Optional, Dict, Any
from datetime import datetime
from fastapi import APIRouter, HTTPException, Depends, Query, Body, Request
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from pydantic import BaseModel, Field
import motor.motor_asyncio
from bson import ObjectId

from ..models.api_key_model import (
    APIKey, KeyProvider, KeyStatus, QuotaLimits, 
    SharedUser, PermissionLevel, AuditLog
)
from ..security.encryption import KeyEncryptor, SecureString, validate_key_strength
from ..permissions.rbac import Permission, rbac_manager
from ..core.config import get_settings
from ..core.auth import get_current_user  # Assume auth is implemented
from ..notifications.key_rotation import rotation_checker, rotate_key_workflow

router = APIRouter(prefix="/api/v1/keys", tags=["API Keys"])
security = HTTPBearer()


# MongoDB client (should be injected as dependency in production)
async def get_db():
    """Get MongoDB database instance."""
    settings = get_settings()
    client = motor.motor_asyncio.AsyncIOMotorClient(settings.MONGODB_URL)
    return client.ai_writing


# Request/Response models
class CreateKeyRequest(BaseModel):
    """Request model for creating API key."""
    provider: str = Field(..., description="Provider name (openai, anthropic, etc.)")
    key_name: str = Field(..., description="Friendly name for the key")
    api_key: str = Field(..., description="The actual API key")
    expires_at: Optional[datetime] = Field(None, description="Optional expiration date")
    quota_limits: Optional[Dict[str, Any]] = Field(None, description="Optional quota limits")


class UpdateKeyRequest(BaseModel):
    """Request model for updating API key."""
    key_name: Optional[str] = Field(None, description="New friendly name")
    status: Optional[str] = Field(None, description="New status")
    expires_at: Optional[datetime] = Field(None, description="New expiration date")
    quota_limits: Optional[Dict[str, Any]] = Field(None, description="New quota limits")


class KeyResponse(BaseModel):
    """Response model for API key."""
    id: str
    provider: str
    key_name: str
    status: str
    created_at: datetime
    last_used_at: Optional[datetime]
    expires_at: Optional[datetime]
    masked_key: str
    usage_stats: Dict[str, Any]
    quota_limits: Dict[str, Any]
    metadata: Dict[str, Any]


class ShareKeyRequest(BaseModel):
    """Request model for sharing API key."""
    user_id: str = Field(..., description="User ID to share with")
    permissions: List[str] = Field(..., description="Permission levels")


@router.post("/", response_model=KeyResponse)
async def create_api_key(
    request: CreateKeyRequest,
    current_user: Dict = Depends(get_current_user),
    db=Depends(get_db),
    req: Request = None
):
    """Create a new API key."""
    # Validate key strength
    is_valid, error_msg = validate_key_strength(request.api_key)
    if not is_valid:
        raise HTTPException(status_code=400, detail=f"Invalid API key: {error_msg}")
    
    # Initialize encryptor
    encryptor = KeyEncryptor()
    
    # Encrypt the key
    encrypted_key, key_hash = encryptor.encrypt(request.api_key)
    
    # Check for duplicate key
    existing = await db.api_keys.find_one({"key_hash": key_hash})
    if existing:
        raise HTTPException(status_code=409, detail="This API key already exists")
    
    # Create API key model
    api_key = APIKey(
        user_id=current_user["id"],
        provider=KeyProvider(request.provider),
        key_name=request.key_name,
        encrypted_key=encrypted_key,
        key_hash=key_hash,
        status=KeyStatus.ACTIVE,
        expires_at=request.expires_at
    )
    
    # Set quota limits if provided
    if request.quota_limits:
        api_key.quota_limits = QuotaLimits(**request.quota_limits)
    
    # Save to database
    result = await db.api_keys.insert_one(api_key.to_dict())
    api_key._id = result.inserted_id
    
    # Create audit log
    audit_log = AuditLog(
        user_id=current_user["id"],
        action="create",
        resource_id=str(result.inserted_id),
        details={"provider": request.provider, "key_name": request.key_name},
        ip_address=req.client.host if req else None,
        user_agent=req.headers.get("User-Agent") if req else None
    )
    await db.audit_logs.insert_one(audit_log.to_dict())
    
    # Clear sensitive data
    with SecureString(request.api_key) as secure_key:
        pass  # Key is automatically cleared when context exits
    
    return KeyResponse(
        id=str(api_key._id),
        provider=api_key.provider.value,
        key_name=api_key.key_name,
        status=api_key.status.value,
        created_at=api_key.created_at,
        last_used_at=api_key.last_used_at,
        expires_at=api_key.expires_at,
        masked_key=api_key.mask_key(),
        usage_stats=api_key.usage_stats.to_dict(),
        quota_limits=api_key.quota_limits.to_dict(),
        metadata=api_key.metadata.to_dict()
    )


@router.get("/", response_model=List[KeyResponse])
async def list_api_keys(
    provider: Optional[str] = Query(None, description="Filter by provider"),
    status: Optional[str] = Query(None, description="Filter by status"),
    skip: int = Query(0, ge=0, description="Number of items to skip"),
    limit: int = Query(20, ge=1, le=100, description="Number of items to return"),
    current_user: Dict = Depends(get_current_user),
    db=Depends(get_db)
):
    """List all API keys for the current user."""
    # Build query filter
    query = {"user_id": current_user["id"]}
    if provider:
        query["provider"] = provider
    if status:
        query["status"] = status
    
    # Query database
    cursor = db.api_keys.find(query).skip(skip).limit(limit).sort("created_at", -1)
    keys = []
    
    async for doc in cursor:
        api_key = APIKey.from_dict(doc)
        keys.append(KeyResponse(
            id=str(api_key._id),
            provider=api_key.provider.value,
            key_name=api_key.key_name,
            status=api_key.status.value,
            created_at=api_key.created_at,
            last_used_at=api_key.last_used_at,
            expires_at=api_key.expires_at,
            masked_key=api_key.mask_key(),
            usage_stats=api_key.usage_stats.to_dict(),
            quota_limits=api_key.quota_limits.to_dict(),
            metadata=api_key.metadata.to_dict()
        ))
    
    return keys


@router.get("/{key_id}", response_model=KeyResponse)
async def get_api_key(
    key_id: str,
    current_user: Dict = Depends(get_current_user),
    db=Depends(get_db),
    req: Request = None
):
    """Get API key details."""
    # Check permission
    rbac_manager.mongodb = db.client
    has_permission, error = await rbac_manager.check_permission(
        current_user["id"], key_id, Permission.READ
    )
    if not has_permission:
        raise HTTPException(status_code=403, detail=error)
    
    # Query database
    doc = await db.api_keys.find_one({"_id": ObjectId(key_id)})
    
    if not doc:
        raise HTTPException(status_code=404, detail="API key not found")
    
    api_key = APIKey.from_dict(doc)
    
    # Create audit log for viewing
    audit_log = AuditLog(
        user_id=current_user["id"],
        action="view",
        resource_id=key_id,
        ip_address=req.client.host if req else None,
        user_agent=req.headers.get("User-Agent") if req else None
    )
    await db.audit_logs.insert_one(audit_log.to_dict())
    
    return KeyResponse(
        id=str(api_key._id),
        provider=api_key.provider.value,
        key_name=api_key.key_name,
        status=api_key.status.value,
        created_at=api_key.created_at,
        last_used_at=api_key.last_used_at,
        expires_at=api_key.expires_at,
        masked_key=api_key.mask_key(),
        usage_stats=api_key.usage_stats.to_dict(),
        quota_limits=api_key.quota_limits.to_dict(),
        metadata=api_key.metadata.to_dict()
    )


@router.put("/{key_id}", response_model=KeyResponse)
async def update_api_key(
    key_id: str,
    request: UpdateKeyRequest,
    current_user: Dict = Depends(get_current_user),
    db=Depends(get_db),
    req: Request = None
):
    """Update API key information."""
    # Find existing key
    doc = await db.api_keys.find_one({
        "_id": ObjectId(key_id),
        "user_id": current_user["id"]
    })
    
    if not doc:
        raise HTTPException(status_code=404, detail="API key not found")
    
    # Build update document
    update_doc = {}
    if request.key_name is not None:
        update_doc["key_name"] = request.key_name
    if request.status is not None:
        update_doc["status"] = request.status
    if request.expires_at is not None:
        update_doc["expires_at"] = request.expires_at
    if request.quota_limits is not None:
        update_doc["quota_limits"] = request.quota_limits
    
    # Update in database
    if update_doc:
        await db.api_keys.update_one(
            {"_id": ObjectId(key_id)},
            {"$set": update_doc}
        )
    
    # Get updated document
    updated_doc = await db.api_keys.find_one({"_id": ObjectId(key_id)})
    api_key = APIKey.from_dict(updated_doc)
    
    # Create audit log
    audit_log = AuditLog(
        user_id=current_user["id"],
        action="update",
        resource_id=key_id,
        details=update_doc,
        ip_address=req.client.host if req else None,
        user_agent=req.headers.get("User-Agent") if req else None
    )
    await db.audit_logs.insert_one(audit_log.to_dict())
    
    return KeyResponse(
        id=str(api_key._id),
        provider=api_key.provider.value,
        key_name=api_key.key_name,
        status=api_key.status.value,
        created_at=api_key.created_at,
        last_used_at=api_key.last_used_at,
        expires_at=api_key.expires_at,
        masked_key=api_key.mask_key(),
        usage_stats=api_key.usage_stats.to_dict(),
        quota_limits=api_key.quota_limits.to_dict(),
        metadata=api_key.metadata.to_dict()
    )


@router.delete("/{key_id}")
async def delete_api_key(
    key_id: str,
    current_user: Dict = Depends(get_current_user),
    db=Depends(get_db),
    req: Request = None
):
    """Delete (soft delete) an API key."""
    # Find existing key
    doc = await db.api_keys.find_one({
        "_id": ObjectId(key_id),
        "user_id": current_user["id"]
    })
    
    if not doc:
        raise HTTPException(status_code=404, detail="API key not found")
    
    # Soft delete by updating status
    await db.api_keys.update_one(
        {"_id": ObjectId(key_id)},
        {"$set": {"status": KeyStatus.REVOKED.value}}
    )
    
    # Create audit log
    audit_log = AuditLog(
        user_id=current_user["id"],
        action="delete",
        resource_id=key_id,
        ip_address=req.client.host if req else None,
        user_agent=req.headers.get("User-Agent") if req else None
    )
    await db.audit_logs.insert_one(audit_log.to_dict())
    
    return {"message": "API key deleted successfully"}


@router.post("/{key_id}/reveal")
async def reveal_api_key(
    key_id: str,
    password: str = Body(..., description="User password for verification"),
    current_user: Dict = Depends(get_current_user),
    db=Depends(get_db),
    req: Request = None
):
    """Reveal the actual API key (requires password verification)."""
    # Check permission
    rbac_manager.mongodb = db.client
    has_permission, error = await rbac_manager.check_permission(
        current_user["id"], key_id, Permission.REVEAL
    )
    if not has_permission:
        raise HTTPException(status_code=403, detail=error)
    
    # TODO: Implement password verification against auth service
    # For now, we'll skip the actual password check
    
    # Find key
    doc = await db.api_keys.find_one({"_id": ObjectId(key_id)})
    
    if not doc:
        raise HTTPException(status_code=404, detail="API key not found")
    
    api_key = APIKey.from_dict(doc)
    
    # Decrypt the key
    encryptor = KeyEncryptor()
    try:
        decrypted_key = encryptor.decrypt(api_key.encrypted_key)
    except ValueError as e:
        raise HTTPException(status_code=500, detail="Failed to decrypt key")
    
    # Create audit log for reveal action
    audit_log = AuditLog(
        user_id=current_user["id"],
        action="reveal",
        resource_id=key_id,
        details={"key_name": api_key.key_name},
        ip_address=req.client.host if req else None,
        user_agent=req.headers.get("User-Agent") if req else None
    )
    await db.audit_logs.insert_one(audit_log.to_dict())
    
    # Return in secure wrapper
    return {
        "api_key": decrypted_key,
        "warning": "This key will only be shown once. Please store it securely."
    }


@router.post("/{key_id}/share")
async def share_api_key(
    key_id: str,
    request: ShareKeyRequest,
    current_user: Dict = Depends(get_current_user),
    db=Depends(get_db),
    req: Request = None
):
    """Share API key with another user."""
    # Find key
    doc = await db.api_keys.find_one({
        "_id": ObjectId(key_id),
        "user_id": current_user["id"]
    })
    
    if not doc:
        raise HTTPException(status_code=404, detail="API key not found")
    
    # Add shared user
    shared_user = SharedUser(
        user_id=request.user_id,
        permissions=request.permissions
    )
    
    await db.api_keys.update_one(
        {"_id": ObjectId(key_id)},
        {"$push": {"shared_with": shared_user.to_dict()}}
    )
    
    # Create audit log
    audit_log = AuditLog(
        user_id=current_user["id"],
        action="share",
        resource_id=key_id,
        details={
            "shared_with": request.user_id,
            "permissions": request.permissions
        },
        ip_address=req.client.host if req else None,
        user_agent=req.headers.get("User-Agent") if req else None
    )
    await db.audit_logs.insert_one(audit_log.to_dict())
    
    return {"message": "API key shared successfully"}


@router.delete("/{key_id}/share/{user_id}")
async def unshare_api_key(
    key_id: str,
    user_id: str,
    current_user: Dict = Depends(get_current_user),
    db=Depends(get_db),
    req: Request = None
):
    """Remove sharing for an API key."""
    # Find key
    doc = await db.api_keys.find_one({
        "_id": ObjectId(key_id),
        "user_id": current_user["id"]
    })
    
    if not doc:
        raise HTTPException(status_code=404, detail="API key not found")
    
    # Remove shared user
    await db.api_keys.update_one(
        {"_id": ObjectId(key_id)},
        {"$pull": {"shared_with": {"user_id": user_id}}}
    )
    
    # Create audit log
    audit_log = AuditLog(
        user_id=current_user["id"],
        action="unshare",
        resource_id=key_id,
        details={"removed_user": user_id},
        ip_address=req.client.host if req else None,
        user_agent=req.headers.get("User-Agent") if req else None
    )
    await db.audit_logs.insert_one(audit_log.to_dict())
    
    return {"message": "Sharing removed successfully"}


# ========== 密钥轮换相关端点 ==========

class RotationStatusResponse(BaseModel):
    """轮换状态响应模型。"""
    key_id: str
    key_name: str
    provider: str
    age_days: int
    urgency: str
    alert_message: str
    recommended_action: str


class RotateKeyRequest(BaseModel):
    """轮换密钥请求。"""
    new_api_key: str = Field(..., description="新的API密钥")
    rotation_reason: Optional[str] = Field("manual_rotation", description="轮换原因")


@router.get("/rotation-status", response_model=List[RotationStatusResponse])
async def get_rotation_status(
    current_user: Dict = Depends(get_current_user),
    db=Depends(get_db)
):
    """获取用户密钥的轮换状态。"""
    try:
        # 初始化轮换检查器
        rotation_checker.mongodb = db.client
        
        # 获取用户的所有密钥
        user_keys = await db.api_keys.find({"user_id": current_user["id"]}).to_list(length=None)
        
        # 检查轮换状态
        all_alerts = []
        for key_doc in user_keys:
            key_id = str(key_doc["_id"])
            alerts = await rotation_checker.check_rotation_status(key_id)
            all_alerts.extend(alerts)
        
        # 转换为响应格式
        response = []
        for alert in all_alerts:
            response.append(RotationStatusResponse(
                key_id=alert.key_id,
                key_name=alert.key_name,
                provider=alert.provider,
                age_days=alert.age_days,
                urgency=alert.urgency.value,
                alert_message=alert.alert_message,
                recommended_action=alert.recommended_action
            ))
        
        return response
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取轮换状态失败: {str(e)}")


@router.post("/{key_id}/rotate")
async def rotate_key(
    key_id: str,
    request: RotateKeyRequest,
    current_user: Dict = Depends(get_current_user),
    db=Depends(get_db),
    req: Request = None
):
    """轮换API密钥。"""
    try:
        # 验证密钥所有权
        doc = await db.api_keys.find_one({
            "_id": ObjectId(key_id),
            "user_id": current_user["id"]
        })
        
        if not doc:
            raise HTTPException(status_code=404, detail="API key not found")
        
        # 初始化轮换检查器
        rotation_checker.mongodb = db.client
        
        # 执行轮换工作流
        result = await rotate_key_workflow(key_id, request.new_api_key)
        
        if not result["success"]:
            raise HTTPException(status_code=400, detail=result["error"])
        
        # 创建审计日志
        audit_log = AuditLog(
            user_id=current_user["id"],
            action="rotate",
            resource_id=key_id,
            details={
                "rotation_reason": request.rotation_reason,
                "previous_key_hash": doc.get("key_hash", "unknown")
            },
            ip_address=req.client.host if req else None,
            user_agent=req.headers.get("User-Agent") if req else None
        )
        await db.audit_logs.insert_one(audit_log.to_dict())
        
        return {"message": result["message"], "key_id": key_id}
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"密钥轮换失败: {str(e)}")


@router.get("/rotation-reminders")
async def get_rotation_reminders(
    include_dismissed: bool = Query(False, description="是否包含已忽略的提醒"),
    current_user: Dict = Depends(get_current_user),
    db=Depends(get_db)
):
    """获取用户的轮换提醒。"""
    try:
        # 初始化轮换检查器
        rotation_checker.mongodb = db.client
        
        # 获取提醒
        reminders = await rotation_checker.get_user_reminders(
            current_user["id"], 
            include_dismissed
        )
        
        return {"reminders": reminders, "count": len(reminders)}
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取提醒失败: {str(e)}")


@router.post("/rotation-reminders/{reminder_id}/dismiss")
async def dismiss_rotation_reminder(
    reminder_id: str,
    current_user: Dict = Depends(get_current_user),
    db=Depends(get_db)
):
    """忽略轮换提醒。"""
    try:
        # 初始化轮换检查器
        rotation_checker.mongodb = db.client
        
        # 忽略提醒
        success = await rotation_checker.dismiss_reminder(reminder_id, current_user["id"])
        
        if not success:
            raise HTTPException(status_code=404, detail="提醒不存在或已被处理")
        
        return {"message": "提醒已忽略"}
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"忽略提醒失败: {str(e)}")


@router.post("/rotation-reminders/{reminder_id}/read")
async def mark_reminder_read(
    reminder_id: str,
    current_user: Dict = Depends(get_current_user),
    db=Depends(get_db)
):
    """标记提醒为已读。"""
    try:
        # 初始化轮换检查器
        rotation_checker.mongodb = db.client
        
        # 标记已读
        success = await rotation_checker.mark_reminder_read(reminder_id, current_user["id"])
        
        if not success:
            raise HTTPException(status_code=404, detail="提醒不存在")
        
        return {"message": "提醒已标记为已读"}
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"标记已读失败: {str(e)}")